package boardgamer.yavalath.client;

import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.RectF;
import android.graphics.Typeface;
import boardgamer.yavalath.client.resources.ImageHandler;
import boardgamer.yavalath.common.YavalathPlayerState;

import static com.google.common.base.Preconditions.checkNotNull;

/**
 * Static methods and constants to help rendering
 *
 * @author garysoed
 */
public class RenderHelper {

  /**
   * The ratio between:
   * <br/>
   * Distance between the centers of two hex tiles when arranged with the closest sides
   * parallel to each other
   * <br/>
   * to the perpendicular distance from the center of a hex to one of the sides, multiplied by 2
   */
  private static final float DISTANCE_HEIGHT_RATIO = 1.125f;

  /**
   * Ratio between the size of the hex letter to the height of the hex
   */
  private static final float HEX_LETTER_SIZE_TILE_HEIGHT_RATIO = 0.5f;

  private static final int MENU_BUTTON_COLOR = Color.rgb(128, 0, 255);

  private static RenderHelper INSTANCE;

  public static void initialize(Bitmap horizontalHexBitmap) {
    INSTANCE = new RenderHelper(horizontalHexBitmap);
  }

  public static RenderHelper instance() {
    return checkNotNull(INSTANCE, "RenderHelper has not been initialized");
  }

  /**
   * Distance between the centers of two hex tiles when arranged with the closest sides parallel
   * to each other
   */
  private final float hexDistance;
  private final Paint hexLetterPaint;

  public RenderHelper(Bitmap horizontalHexBitmap) {
    this.hexDistance = horizontalHexBitmap.getHeight() * DISTANCE_HEIGHT_RATIO;
    this.hexLetterPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    this.hexLetterPaint.setColor(Color.BLACK);
    this.hexLetterPaint.setTypeface(getHexLetterFont());
    this.hexLetterPaint.setTextSize(
        horizontalHexBitmap.getHeight() * HEX_LETTER_SIZE_TILE_HEIGHT_RATIO);
  }

  public float getHexDistance() {
    return hexDistance;
  }

  public int getMenuButtonColor() {
    return MENU_BUTTON_COLOR;
  }

  public Paint getHexLetterPaint() {
    return new Paint(hexLetterPaint);
  }

  public Typeface getHexLetterFont() {
    return ImageHandler.headingFont;
  }

  // Displacements for horizontal hex tile (width is longer than the height)
  public PointF getHHexUpRightDisplacement() {
    return new PointF(((float) Math.sqrt(3)) / 2f * hexDistance, -0.5f * hexDistance);
  }

  public PointF getHHexDownRightDisplacement() {
    return new PointF(((float) Math.sqrt(3)) / 2f * hexDistance, 0.5f * hexDistance);
  }

  public PointF getHHexDownDisplacement() {
    return new PointF(0, hexDistance);
  }

  // Displacements for vertical hex tiles (height is longer than the width)
  public PointF getVHexUpRightDisplacement() {
    return new PointF(0.5f * hexDistance, -((float) Math.sqrt(3)) / 2f * hexDistance);
  }

  public PointF getVHexDownRightDisplacement() {
    return new PointF(0.5f * hexDistance, ((float) Math.sqrt(3)) / 2f * hexDistance);
  }

  public PointF getVHexRightDisplacement() {
    return new PointF(hexDistance, 0);
  }

  public int getColor(YavalathPlayerState.PlayerColor c) {
    switch (c) {
      case RED:
        return Color.rgb(255, 0, 0);
      case ORANGE:
        return Color.rgb(255, 128, 0);
      case YELLOW:
        return Color.rgb(255, 192, 0);
      case CHARTREUSE:
        return Color.rgb(128, 192, 0);
      case AQUA:
        return Color.rgb(0, 192, 128);
      case BLUE:
        return Color.rgb(0, 128, 255);
      case MAGENTA:
        return Color.rgb(255, 0, 255);
      case BROWN:
        return Color.rgb(150, 75, 0);
      case WHITE:
        return Color.rgb(192, 192, 192);
      default:
        throw new UnsupportedOperationException("Color not supported: " + c);
    }
  }

  /**
   * Updates the path passed in with a horizontal hexagon
   *
   * @param hex                 Hexagon path to update
   * @param boundingRect        Bounding rectangle to fit the hexagon in
   * @param innerVerticesOffset Offset of the 4 inner vertices from the closest edge of the
   *                            bounding rectangle.
   */
  public void updateHoriHexPath(Path hex, RectF boundingRect, float innerVerticesOffset) {
    float left0 = boundingRect.left;
    float left1 = boundingRect.left + innerVerticesOffset;
    float left2 = boundingRect.right - innerVerticesOffset;
    float left3 = boundingRect.right;

    float top0 = boundingRect.top;
    float top1 = boundingRect.centerY();
    float top2 = boundingRect.bottom;

    hex.reset();
    hex.moveTo(left0, top1);
    hex.lineTo(left1, top0);
    hex.lineTo(left2, top0);
    hex.lineTo(left3, top1);
    hex.lineTo(left2, top2);
    hex.lineTo(left1, top2);
    hex.close();
  }

  public float getMaxFittingHeightTextSize(float maxHeight, Typeface font) {
    RectF bound = ImageHandler.boundSizeRatio.get(font);
    return maxHeight / bound.height();
  }
}
